/**
 *
 *  Copyright (c) 2011, AstroDAbis
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without modification,
 *  are permitted provided that the following conditions are met:
 *
 *      * Redistributions of source code must retain the above copyright notice,
 *        this list of conditions and the following disclaimer.
 *      * Redistributions in binary form must reproduce the above copyright notice,
 *        this list of conditions and the following disclaimer in the documentation
 *        and/or other materials provided with the distribution.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 *  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 *  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 *  OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
package org.jiscinvolve.astrodabis.mallow.control;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.stereotype.Controller;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.request.RequestAttributes;

import org.springframework.web.bind.annotation.ModelAttribute;

import org.springframework.beans.factory.annotation.Autowired;  

import org.jiscinvolve.astrodabis.mallow.model.api.Tag;
import org.jiscinvolve.astrodabis.mallow.model.api.Query;
import org.jiscinvolve.astrodabis.mallow.model.api.Result;
import org.jiscinvolve.astrodabis.mallow.model.api.Account;

import org.jiscinvolve.astrodabis.mallow.model.tap.TapTable;
import org.jiscinvolve.astrodabis.mallow.model.tap.TapObject;
import org.jiscinvolve.astrodabis.mallow.model.tap.TapService;

import org.jiscinvolve.astrodabis.mallow.model.jpa.PersistentManager;

/**
 * Base class for our MVC controllers.
 *
 */
@Controller
public class BaseController
    {

    /**
     * Our debug logger.
     * 
     */
    private static Log log = LogFactory.getLog(BaseController.class);

    /**
     * Object not found view.
     * 
     */
    public static final String NOT_FOUND_VIEW = "not-found" ;

    /**
     * Our PersistentObject manager.
     * 
     */
    @Autowired
    private PersistentManager manager ;

    /**
     * Our PersistentObject manager.
     * 
     */
    public PersistentManager manager()
        {
        if (this.manager == null)
            {
            String message = "Null reference to PersistentManager in BaseController";
            log.error(message);
            throw new IllegalStateException(
                message
                );
            }
        else {
            return this.manager;
            }
        }

    /**
     * Session attribute name for the current user ID.
     * 
     */
    public static final String SESSION_UID = "astrodabis.uid" ;


    /**
     * Get the Session ID from a WebRequest.
     * 
     */
    public String sid(WebRequest request)
        {
        return request.getSessionId();
        }

// Actor

    /**
     * Get the actor from the Request Sesion.
     *
     */
    public Account actor(WebRequest request)
        {
        log.debug("BaseController.actor(WebRequest)");

        //
        // Get the actor UID from our Session.
        log.debug("Checking for UID in web request");
        String uid = (String) request.getAttribute(
            SESSION_UID,
            RequestAttributes.SCOPE_SESSION
            );
        //
        // If the UID is null, return null.
        if (null == uid)
            {
            log.debug("No UID in web request");
            return null ;
            }
        //
        // If the UID is not null.
        else {
            log.debug("Found UID in web request [" + uid + "]");
            //
            // Try loading the actor Account and set the Session attribute.
            try {
                log.debug("Loading actor Account using UID [" + uid + "]");
                Account actor = manager().accounts().select(
                    uid
                    );
                this.actor(
                    request,
                    actor
                    );
                return actor ;
                }
            //
            // Anything goes wrong, log it and return null.
            catch (Exception ouch)
                {
                log.error("Exception while retrieving actor Account [" + uid + "][" + ouch.getClass().getName() + "][" + ouch.getMessage() + "]");
                return null ;
                }            
            }
        }

    /**
     * Set the actor Account in a Reqeust Session (used by login/logout).
     *
     */
    public void actor(WebRequest request, Account actor)
        {
        log.debug("BaseController.actor(WebRequest, Account)");
        if (actor != null)
            {
            request.setAttribute(
                SESSION_UID,
                actor.ident(),
                RequestAttributes.SCOPE_SESSION
                );
            }
        else {
            request.removeAttribute(
                SESSION_UID,
                RequestAttributes.SCOPE_SESSION
                );
            }
        }

// User

    /**
     * Load a user Account based on name.
     *
     */
    public Account user(String name)
        {
        log.debug("BaseController.user(String)");
        log.debug("  Name [" + name + "]");
        //
        // If we don't have a valid name.
        if (name == null)
            {
            throw new NotFoundException(
                "user"
                );
            }
        //
        // If we have a valid name.
        else {
            //
            // Try loading the Account.
            log.debug("Loading Account for [" + name + "]");
            Account user = manager().accounts().name(
                name
                );
            //
            // If we found a matching Account.
            if (user != null)
                {
                log.debug("Found matching Account [" +  user.ident() + "][" +  user.name() + "]");
                return user ;
                }
            //
            // If we didn't find a matching Account.
            else {
                log.warn("Unable to find user Account [" + name + "]");
                throw new NotFoundException(
                    "user",
                    name
                    );
                }
            }
        }

// Query

    /**
     * Load a Query based on identifier.
     *
     */
    public Query query(String ident)
        {
        log.debug("BaseController.query(String)");
        log.debug("  Ident [" + ident + "]");
        //
        // If we don't have a valid identifier.
        if (ident == null)
            {
            throw new NotFoundException(
                "query"
                );
            }
        //
        // If we have a valid identifier.
        else {
            //
            // Try loading the Query.
            Query query = manager().queries().select(
                ident
                );
            //
            // If we found a matching Query.
            if (query != null)
                {
                log.debug("Found matching Query [" +  query.ident() + "]");
                return query ;
                }
            //
            // If we didn't find a matching Query.
            else {
                log.warn("Unable to find Query [" + ident + "]");
                throw new NotFoundException(
                    "query",
                    ident
                    );
                }
            }
        }

// Tag

    /**
     * Load a Tag based on identifier.
     *
     */
    public Tag tag(String ident)
        {
        log.debug("BaseController.tag(String)");
        log.debug("  Ident [" + ident + "]");
        //
        // If we don't have a valid identifier.
        if (ident == null)
            {
            throw new NotFoundException(
                "tag"
                );
            }
        //
        // If we have a valid identifier.
        else {
            //
            // Try loading the tag.
            Tag tag = manager().tags().select(
                ident
                );
            //
            // If we found a matching Tag.
            if (tag != null)
                {
                log.debug("Found matching Tag [" +  tag.ident() + "]");
                return tag ;
                }
            //
            // If we didn't find a matching Tag.
            else {
                log.warn("Unable to find Tag [" + ident + "]");
                throw new NotFoundException(
                    "tag",
                    ident
                    );
                }
            }
        }

    /**
     * Load a Tag based on user Account and Tag name.
     *
     */
    public Tag tag(Account user, String name)
        {
        log.debug("BaseController.tag(Account, String)");
        log.debug("  User [" + user.name() + "]");
        log.debug("  Name [" + name + "]");

        if (name == null)
            {
            throw new NotFoundException(
                "tag"
                );
            }

        Tag tag = user.tags().name(
            name
            );

        if (tag == null)
            {
            throw new NotFoundException(
                "tag",
                name
                );
            }
        else {
            return tag ;
            }
        }

// Results

    /**
     * Load a Result based on identifier.
     *
     */
    public Result result(String ident)
        {
        log.debug("BaseController.result(String)");
        log.debug("  Ident [" + ident + "]");
        try {
            return result(
                Long.valueOf(
                    ident
                    )
                );
            }
        catch (NumberFormatException ouch)
            {
            throw new NotFoundException(
                "result",
                ident
                );
            }
        }

    /**
     * Load a Result based on identifier.
     *
     */
    public Result result(Long ident)
        {
        log.debug("BaseController.result(Long)");
        log.debug("  Ident [" + ident + "]");
        //
        // If we don't have a valid identifier.
        if (ident == null)
            {
            throw new NotFoundException(
                "result"
                );
            }
        //
        // If we have a valid identifier.
        else {
            //
            // Try loading the Result.
            Result result = manager().results().select(
                ident
                );
            //
            // If we found a matching Result.
            if (result != null)
                {
                log.debug("Found matching Result [" +  result.ident() + "]");
                return result ;
                }
            //
            // If we didn't find a matching Result.
            else {
                log.warn("Unable to find Result [" + ident + "]");
                throw new NotFoundException(
                    "result",
                    ident
                    );
                }
            }
        }


// Service

    /**
     * Load a TapService based on identifier.
     *
     */
    public TapService service(String ident)
        {
        log.debug("BaseController.service(String)");
        log.debug("  Ident [" + ident + "]");
        //
        // If we don't have a valid identifier.
        if (ident == null)
            {
            throw new NotFoundException(
                "service"
                );
            }
        //
        // If we have a valid identifier.
        else {
            //
            // Try loading the TapService.
            TapService service = manager().services().select(
                ident
                );
            //
            // If we found a matching TapService.
            if (service != null)
                {
                log.debug("Found matching TapService [" +  service.ident() + "]");
                return service ;
                }
            //
            // If we didn't find a matching TapService.
            else {
                log.warn("Unable to find TapService [" + ident + "]");
                throw new NotFoundException(
                    "service",
                    ident
                    );
                }
            }
        }

// Table

    /**
     * Load a TapTable based on identifier.
     *
     */
    public TapTable table(String ident)
        {
        log.debug("BaseController.table(String)");
        log.debug("  Ident [" + ident + "]");
        //
        // If we don't have a valid identifier.
        if (ident == null)
            {
            throw new NotFoundException(
                "table"
                );
            }
        //
        // If we have a valid identifier.
        else {
            //
            // Try loading the TapTable.
            TapTable table = manager().tables().select(
                ident
                );
            //
            // If we found a matching TapTable.
            if (table != null)
                {
                log.debug("Found matching TapTable [" +  table.ident() + "]");
                return table ;
                }
            //
            // If we didn't find a matching TapTable.
            else {
                log.warn("Unable to find TapTable [" + ident + "]");
                throw new NotFoundException(
                    "table",
                    ident
                    );
                }
            }
        }


// TapObject

    /**
     * Load a TapObject based on compund identifier.
     *
     */
    public TapObject object(String tid, String oid)
        {
        return new TapObject(
            this.table(
                tid
                ),
            oid            
            );
        }

    }   

